#include <iostream>
#include <string.h>
#include <sys/socket.h>
#include <iomanip>
#include <algorithm>
#include <time.h>
using namespace std;
typedef unsigned int uint;
typedef unsigned char uchar;
#include "sha1.phs.h"
#include "rfcsha.h"


void sha1(unsigned char *data, unsigned int lenght,unsigned char digest[20])
{
    unsigned int  H[5]  = {0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0};
    unsigned char d[64] = {0} ;
    unsigned int  w[80] = {0} ;
    unsigned int i=0;
    
    while(true)
    {
        unsigned int size = std::min(lenght-i,64U);
        memcpy(d,data+i,size);
        i+=size;
        if(size<64)break;
        SHA1ProcessMessageBlock::generatrix<>::code(d, w, H);
    }
    i=i%64;
    
    /*padding*/
    if (i > 55)
    {
        d[i++] = 0x80;
        while(i < 64)
        {  d[i++] = 0; }
        SHA1ProcessMessageBlock::generatrix<>::code(d, w, H);
        while(i < 56)
        { d[i++] = 0; }
    }
    else
    {
        d[i++] = 0x80;
        while(i < 56)
        { d[i++] = 0; }
    }

    unsigned int Length_High = 0; 
    unsigned int Length_Low  = lenght*8;
    d[56] = Length_High >> 24;
    d[57] = Length_High >> 16;
    d[58] = Length_High >> 8;
    d[59] = Length_High;
    d[60] = Length_Low >> 24;
    d[61] = Length_Low >> 16;
    d[62] = Length_Low >> 8;
    d[63] = Length_Low;

    SHA1ProcessMessageBlock::generatrix<>::code(d, w, H);

     for (unsigned int i = 0; i < 5; i++)
                 ((unsigned int *)digest)[i] = H[i];   
}

void print_digest(unsigned char digest[20])
{
 for (unsigned int i = 0; i < 20; i++)
     {
      unsigned char c = digest[i];
      cout << hex << setw(2) << setfill('0') << (unsigned int)c;
     }
     cout << endl << dec;
}
/*
unsigned long diff(timespec end, timespec start)
{
	timespec temp;
	if ((end.tv_nsec-start.tv_nsec)<0) {
		temp.tv_sec = end.tv_sec-start.tv_sec-1;
		temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
	} else {
		temp.tv_sec = end.tv_sec-start.tv_sec;
		temp.tv_nsec = end.tv_nsec-start.tv_nsec;
	}
	return temp.tv_nsec/100000;
}*/

struct timespec timespec_sub(struct timespec timespec_1, struct timespec timespec_2) {       
	struct timespec rtn_val;
	int xsec;
	int sign = 1;

	if ( timespec_2.tv_nsec > timespec_1.tv_nsec ) {
		xsec = (int)((timespec_2.tv_nsec - timespec_1.tv_nsec) / (1E9 + 1));
		timespec_2.tv_nsec -= (long int)(1E9 * xsec);
		timespec_2.tv_sec += xsec;
	}

	if ( (timespec_1.tv_nsec - timespec_2.tv_nsec) > 1E9 ) {
		xsec = (int)((timespec_1.tv_nsec - timespec_2.tv_nsec) / 1E9);
		timespec_2.tv_nsec += (long int)(1E9 * xsec);
		timespec_2.tv_sec -= xsec;
	}

	rtn_val.tv_sec = timespec_1.tv_sec - timespec_2.tv_sec;
	rtn_val.tv_nsec = timespec_1.tv_nsec - timespec_2.tv_nsec;                                                 

	if (timespec_1.tv_sec < timespec_2.tv_sec) {
		sign = -1;
	}

	rtn_val.tv_sec = rtn_val.tv_sec * sign;
        rtn_val.tv_nsec = rtn_val.tv_nsec * sign;

	return rtn_val;
}

double diff(struct timespec clock_1, struct timespec clock_2) {
	double rtn_val;
	struct timespec diff;

	diff = timespec_sub(clock_1, clock_2);
	
	rtn_val = diff.tv_sec;
	rtn_val += (double)diff.tv_nsec/(double)1E9;

	return rtn_val;		
}

int main()
{
    unsigned char data[64]={0};
    unsigned int lenght=64;
    unsigned char digest[20]={0};
    #define SAMPLES 2u
    double  resultrfc[SAMPLES];
    double resultactive[SAMPLES];
    unsigned int sizes[SAMPLES];
    const unsigned long max_size_data= ((1024*1024*256)*SAMPLES)+(1024*1024*512);
    unsigned char* bigData = new unsigned char[max_size_data];
    timespec b2,e2;
    timespec b,e;
    for(unsigned int i=0;i<SAMPLES;++i)
   {
    const unsigned long size_data= ((1024*1024*256)*i)+(1024*1024*512);
   
    
    std::cout << "N="<< i << "  Size: "<< (size_data/(1024*1024)) << "MB:"<<std::endl;

    //active library version 
    memset ( bigData, 123, size_data );
    std::cout << "active library version:" <<std::endl;
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &b);
    sha1(bigData, size_data, digest);
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &e);
    print_digest(digest);
    std::cout << "time :"<<diff(e,b) <<std::endl;

    //rfc version
    memset ( bigData, 123, size_data );
    std::cout << "rfc version:" <<std::endl;
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &b2);
    rfcsha1(bigData, size_data, digest);
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &e2);
    print_digest(digest);
    std::cout << "time :"<< diff(e2,b2) <<std::endl;
    
   
    
   
    resultrfc[i]   =diff(e2,b2);
    resultactive[i]=diff(e,b);
    sizes[i]       =size_data;

    std::cout << "size: "<< (sizes[i]/(1024*1024)) << "MB | time rfc: "<<resultrfc[i]<< " | time active:"<< resultactive[i] << " | time diff:"<< resultrfc[i]-resultactive[i]<<std::endl;

//    delete bigData;   
    }    
    std::cout<< std::endl << std::endl << "summary: " << std::endl;
    for(unsigned int i=0;i<SAMPLES;++i)
    {
         std::cout << "size: "<< (sizes[i]/(1024*1024)) << "MB | time rfc: "<<resultrfc[i]<< " | time active:"<< resultactive[i] << " | time diff:"<< resultrfc[i]-resultactive[i]<<std::endl;
    }
    return 0;
}
